/**

This file is part of MaCI/GIMnet.

MaCI/GIMnet is free software: you can redistribute it and/or modify it 
under the terms of the GNU Lesser General Public License as published 
by the Free Software Foundation, either version 3 of the License, or 
(at your option) any later version.

MaCI/GIMnet is distributed in the hope that it will be useful, but WITHOUT 
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public 
License for more details.

You should have received a copy of the GNU Lesser General Public 
License along with GIMnet. (See COPYING.LESSER) If not, see 
<http://www.gnu.org/licenses/>.

**/
#ifndef DEVICES_H_
#define DEVICES_H_

#include <GL/gl.h>
#include <GL/glut.h>

#include "MachineUnit.h"
#include "DeviceLayer.h"
#include "RangingClient.hpp"
#include "SpeedCtrlClient.hpp"
#include "CoordinateDriveClient.hpp"
#include "PositionClient.hpp"
#include "MapClient.hpp"
#include "AlarmClient.hpp"
#include "EnergyClient.hpp"
#include "sync.hpp"


#include "Mapper.hpp"

//forward declarations
/*namespace MaCI
{

namespace Position
{
class CPositionClient;
};
namespace SpeedCtrl
{
class CSpeedCtrlClient;
};

namespace Ranging
{
class CRangingClient;
};

};*/

namespace MaCIGUI
{

#define UNIT_COLOR_SELECTED 1.0, 1.0, 0.0
#define UNIT_COLOR_DESELECTED 0.7, 0.7, 0.0
#define LINE_WIDTH_SELECTED 2
#define LINE_WIDTH_DESELECTED 1

#define UNIT_LOCALIZE_COLOR_SELECTED 0.0, 0.0, 1.0
#define UNIT_LOCALIZE_COLOR_DESELECTED 0.0, 0.0, 0.7

#define ICON_MODE_J2B2  1
#define ICON_MODE_AVANT 2

#define MAP_LINE_COLOR 0.3, 0.3, 1.0

/** PositionLayer **/

#define BREADCRUMB_LIFETIME_S 30
  
class CPositionLayer : public CDeviceLayer
{
  public:
    CPositionLayer(std::string Name, CMachineUnit* unit, 
                  MaCI::Position::CPositionClient* positionClient);

    ~CPositionLayer();

    void Draw(float viewLeft, float viewRight, float viewTop, float viewBottom, 
              float scale);//GLuint renderMode, float alpha);

    void OnClick(float x, float y);
    void OnKey(int keyCode);
    void OnKeyUp(int keyCode);
    void OnKillFocus();
    
    void Update(int timeout);

    void GLTransform();
    float GetX();
    float GetY();
    float positionX, positionY, positionZ;
    float heading;

    int iconMode;
  private:
    struct posWTime
    {
      MaCI::Position::CPositionData position;
      MaCI::Common::TTimestamp time;
    };
    
    //For position initializing
    bool iPositionSelected,iHeadingSelected;
    float iInitX,iInitY,iInitXEnd,iInitYEnd;
    //
    int seq;
    
    bool enableBreadCrumbs;
    ownMutexHandle bcMutex;
    std::list<posWTime> breadCrumbs;
};


/** RangingLayer **/

class CRangingLayer : public CDeviceLayer
{
  public:
    CRangingLayer(std::string Name, CMachineUnit* unit, 
                  MaCI::Ranging::CRangingClient* rangingClient);

    ~CRangingLayer();

    void Draw(float viewLeft, float viewRight, float viewTop, float viewBottom, 
              float scale);//GLuint renderMode, float alpha);

    void OnClick(float x, float y);
    void OnKey(int keyCode);
    void OnKeyUp(int keyCode);
    void OnKillFocus();
    
    void Update(int timeout);

    void GLTransform();
    float GetX();
    float GetY();
    
  private:
    ownMutexHandle distArrayMutex;
    MaCI::Ranging::TDistanceArray distArray;
    gim::binbag::CBinBag binBag;
    int seq;
};


class CSpeedCtrlLayer : public CDeviceLayer
{
  public:
    CSpeedCtrlLayer(std::string Name, CMachineUnit* unit, 
                    MaCI::SpeedCtrl::CSpeedCtrlClient* speedCtrlClient);
    
    ~CSpeedCtrlLayer();
    
    void Draw(float viewLeft, float viewRight, float viewTop, float viewBottom, 
                  float scale);//GLuint renderMode, float alpha);

    void OnClick(float x, float y);
    void OnKey(int keyCode);
    void OnKeyUp(int keyCode);
    void OnKillFocus();
    
    void Update(int timeout);

    void GLTransform();
    float GetX();
    float GetY();
    
    const static float SPEED_STEP;
    const static float ANGULAR_SPEED_STEP;
    const static float ACCELERATION;
    
  private:
    float speed, angularSpeed;
    int multiplier;
};

class CCoordinateDriveLayer : public CDeviceLayer
{
 
  public:
  CCoordinateDriveLayer(std::string Name, CMachineUnit* unit, 
			MaCI::CoordinateDrive::CCoordinateDriveClient* coordinateDriveClient);
    
    ~CCoordinateDriveLayer();
    //void drawText(float x, float y, void *font, char *string);
    void Draw(float viewLeft, float viewRight, float viewTop, float viewBottom, 
                  float scale);//GLuint renderMode, float alpha);
    void PrintHelp(float viewLeft, float viewRight, float viewTop, 
                  float viewBottom, float scale);
    void PrintInfos(float viewLeft, float viewRight, float viewTop, 
		    float viewBottom, float scale);
    void OnClick(float x, float y);
    void OnKey(int keyCode);
    void OnKeyUp(int keyCode);
    void OnKillFocus();
    
    void Update(int timeout);

    void GLTransform();
    float GetX();
    float GetY();
    
  
    
  private:
    struct TCoordinate{
      TCoordinate(float aX = 0.0,
		  float aY = 0.0,
		  float aA = 0.0,
		  bool aIsWaitPoint= false,
		  bool aIsPathTarget = false) 
	:  x(aX),
	   y(aY),
	   a(aA),
	   isWaitPoint(aIsWaitPoint),
	   isPathTarget(aIsPathTarget) {}
      
      float x;
      float y;
      float a;
      bool isWaitPoint;
      bool isPathTarget;
    };
    bool pathModeCoordinate;            //Tells if the current point selection is in path mode
    bool currentlyOnWaitPoint; //Tells if the robot is currently on a wait point
    std::vector<TCoordinate> selectedPoints;   //The drive points which are sent to the robot
    std::vector<TCoordinate> drivingPoints;   //The drive points which are sent to the robot
    TCoordinate tempPointForAngleMode;
    bool angleModePointSet;                    //Tells if the angleModePoint is set(next point is for defining angle)
    ownMutexHandle drivePointsMutex;
    bool waitPointWanted;
    bool pathModePointWanted;
    bool angleModePointWanted;
    bool drawAngleModePoint;
    bool printInfos;
    bool printHelp;
    int latestPointSeq;
    int coordinateSeq;
    //Modes at coordinateDrive
    bool modeTurnFirstToGoal;
    bool modeUseAngle;
    bool modePathDriving;
    bool modeUseReverse;

    TCoordinate latestPoint;
    
};

/** Map Layer **/

class CMapLayer : public CDeviceLayer , private gim::CSync
{
  public:
    CMapLayer(std::string Name, CMachineUnit* unit, 
                  MaCI::Map::CMapClient* mapClient);

    ~CMapLayer();

    void Draw(float viewLeft, float viewRight, float viewTop, float viewBottom, 
              float scale);//GLuint renderMode, float alpha);

    void OnClick(float x, float y);
    void OnKey(int keyCode);
    void OnKeyUp(int keyCode);
    void OnKillFocus();
    
    void Update(int timeout);

    void GLTransform();
    float GetX();
    float GetY();
   
  private:
    int seq;
    bool updated;
    bool inited;
    float drawTreshold;
    //std::vector<MaCI::Map::CMapData*> mapData;
    CMapper mapper;
    gmbVector groundUpdates;
    occuVector occuUpdates;
    //std::vector<MaCI::Map::TMapLine> lines;
    //std::vector<MaCI::Map::THeightMapBlockHeader> blocks
    //std::vector<float*> data;
    GLuint displayList2D;//,hmDisplayList3D;

};


/** Alarm layer **/

#define ALARM_LIFETIME_S 7.0

#define BUBBLE_W (10/scale)
#define BUBBLE_D (20/scale+0.3)

class CAlarmLayer : public CDeviceLayer
{
  public:
    CAlarmLayer(std::string Name, CMachineUnit* unit, 
                  MaCI::Alarm::CAlarmClient* alarmClient);

    ~CAlarmLayer();

    void Draw(float viewLeft, float viewRight, float viewTop, float viewBottom, 
              float scale);//GLuint renderMode, float alpha);

    void OnClick(float x, float y);
    void OnKey(int keyCode);
    void OnKeyUp(int keyCode);
    void OnKillFocus();
    
    void Update(int timeout);

    void GLTransform();
    float GetX();
    float GetY();

  private:
    struct alarm
    {
      MaCI::Alarm::TAlarmEvent event;
      MaCI::Common::TTimestamp time;
    };
    
    std::list<alarm> alarmArray;
    ownMutexHandle alarmArrayMutex;

};


/** Energy layer **/
/*
#define ICON_W 0.3
#define ICON_H 0.1
*/
#define ICON_W (30.0/scale)
#define ICON_H (10.0/scale)

class CEnergyLayer : public CDeviceLayer
{
  public:
    CEnergyLayer(std::string Name, CMachineUnit* unit, 
                MaCI::Energy::CEnergyClient* energyClient);

    ~CEnergyLayer();

    void Draw(float viewLeft, float viewRight, float viewTop, float viewBottom, 
              float scale);//GLuint renderMode, float alpha);

    void OnClick(float x, float y);
    void OnKey(int keyCode);
    void OnKeyUp(int keyCode);
    void OnKillFocus();
    
    void Update(int timeout);

    void GLTransform();
    float GetX();
    float GetY();

  private:
    unsigned int seq;
    
    bool working;
    float level, maxLevel, consumption;
    
};

}

#endif /*DEVICES_H_*/
